﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System.ServiceModel.Description;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Client;

namespace Ragim.Selfpub.CrmSolution.Plugins.Tools
{
    public static class Util
    {
        public const string OrgName = "t8authors";
        public const string UserName = "ragim.khanukaev@t8authors.onmicrosoft.com";
        public const string Pass = "Zaq12wsx";

        public static OrganizationServiceProxy GetService_online()
        {
            return Util.GetCrmService(Util.GetServiceUrl_online(Util.OrgName), Util.GetCredentials_UsernamePassword(Util.UserName, Util.Pass));
        }

        public static Uri GetServiceUrl_onpremise(string serverName, int port, string organizationName)
        {
            return new Uri(string.Format("{0}:{1}/{2}/XRMServices/2011/Organization.svc", serverName, port, organizationName));
        }

        public static Uri GetServiceUrl_online(string organizationName)
        {
            return new Uri(String.Format("https://{0}.api.crm4.dynamics.com/XrmServices/2011/Organization.svc", organizationName));// new Uri(string.Format("{0}:{1}/{2}/XRMServices/2011/Organization.svc", serverName, port, organizationName));
        }

        public static void AddToDictionary(Dictionary<Guid, int> dic, Guid key, int value)
        {
            if (dic.ContainsKey(key)) dic[key] += value;
            else dic.Add(key, value);
        }

        public static bool CheckEntityName(IPluginExecutionContext context, string entityName)
        {
            return (
                ((context.InputParameters["Target"] is Entity) && (((Entity)context.InputParameters["Target"]).LogicalName == entityName))
                ||
                ((context.InputParameters["Target"] is EntityReference) && (((EntityReference)context.InputParameters["Target"]).LogicalName == entityName))
                );
        }

        //public static object GetFieldValueCRM(this ProtocolOfChangesDLL.DataElement obj)
        //{
        //    if (obj.IsNull ?? false)
        //        return null;
        //    else
        //        switch (obj.FType)
        //        {
        //            case "boolean":
        //                if (obj.BoolValue.HasValue)
        //                    return obj.BoolValue.Value;
        //                else
        //                    return null;
        //            case "decimal":
        //            case "double":
        //                if (obj.DecimalValue.HasValue)
        //                    return obj.DecimalValue.Value;
        //                else
        //                    return null;
        //            case "money":
        //                if (obj.DecimalValue.HasValue)
        //                    return new Money() { Value = obj.DecimalValue.Value };
        //                else
        //                    return null;
        //            case "optionset":
        //                if (obj.IntValue.HasValue)
        //                    return new OptionSetValue() { Value = obj.IntValue.Value };
        //                else
        //                    return null;
        //            case "integer":
        //                if (obj.IntValue.HasValue)
        //                    return obj.IntValue.Value;
        //                else
        //                    return null;
        //            case "datetime":
        //                if (obj.DateTimeValue.HasValue)
        //                    return obj.DateTimeValue.Value;
        //                else
        //                    return null;
        //            case "memo":
        //            case "string":
        //                return obj.StringValue;
        //            case "lookup":
        //                Guid id;

        //                if (Guid.TryParse(obj.LookupIdValue, out id))
        //                {
        //                    return new EntityReference() { Id = id, LogicalName = obj.LookupTypeValue };
        //                }
        //                else
        //                    return null;
        //            default:
        //                return null;
        //        }
        //}

        #region получение аттрибутов
        public static object GetAttribute(Entity target1, Entity target2, Entity target3, string attributeName)
        {
            object value = null;
            if (target1 != null && target1.Contains(attributeName)) value = target1[attributeName];
            else if (target2 != null && target2.Contains(attributeName)) value = target2[attributeName];
            else if (target3 != null && target3.Contains(attributeName)) value = target3[attributeName];

            if (value == null)
                return null;
            else if (value is AliasedValue)
                return (value as AliasedValue).Value;
            else
                return value;
        }

        public static object GetAttribute(this Entity target1, string attributeName)
        {
            return GetAttribute(target1, null, null, attributeName);
        }

        public static decimal? GetMoneyAttributeValue(Entity target1, Entity target2, Entity target3, string attributeName)
        {
            var qqq = GetAttribute(target1, target2, target3, attributeName);
            if (qqq != null)
            {
                return ((Money)qqq).Value;
            }
            else
                return null;
        }
        public static decimal? GetMoneyAttributeValue(this  Entity target, string attributeName)
        {
            return GetMoneyAttributeValue(target, null, null, attributeName);
        }

        public static decimal? GetDecimalAttributeValue(Entity target1, Entity target2, Entity target3, string attributeName)
        {
            var qqq = GetAttribute(target1, target2, target3, attributeName);
            if (qqq != null)
                return (decimal)qqq;
            else
                return null;
        }
        public static decimal? GetDecimalAttributeValue(this  Entity target, string attributeName)
        {
            return GetDecimalAttributeValue(target, null, null, attributeName);
        }

        public static bool? GetBoolAttributeValue(Entity target1, Entity target2, Entity target3, string attributeName)
        {
            var qqq = GetAttribute(target1, target2, target3, attributeName);
            if (qqq != null)
                return (bool)qqq;
            else
                return null;
        }
        public static bool? GetBoolAttributeValue(this  Entity target, string attributeName)
        {
            return GetBoolAttributeValue(target, null, null, attributeName);
        }

        public static int? GetIntAttributeValue(Entity target1, Entity target2, Entity target3, string attributeName)
        {
            var qqq = GetAttribute(target1, target2, target3, attributeName);
            if (qqq != null)
                return (int)qqq;
            else
                return null;
        }
        public static int? GetIntAttributeValue(this  Entity target, string attributeName)
        {
            return GetIntAttributeValue(target, null, null, attributeName);
        }

        public static EntityReference GetEntityReferenceAttributeValue(Entity target1, Entity target2, Entity target3, string attributeName)
        {
            var qqq = GetAttribute(target1, target2, target3, attributeName);
            if (qqq != null)
                return qqq as EntityReference;
            else
                return null;
        }
        public static EntityReference GetEntityReferenceAttributeValue(this  Entity target, string attributeName)
        {
            return GetEntityReferenceAttributeValue(target, null, null, attributeName);
        }

        public static AliasedValue GetAliasedValueAttributeValue(Entity target1, Entity target2, Entity target3, string attributeName)
        {
            object value = null;
            if (target1 != null && target1.Contains(attributeName)) value = target1[attributeName];
            else if (target2 != null && target2.Contains(attributeName)) value = target2[attributeName];
            else if (target3 != null && target3.Contains(attributeName)) value = target3[attributeName];

            if (value == null)
                return null;
            else if (value is AliasedValue)
                return (value as AliasedValue);
            else
                return null;
        }
        public static AliasedValue GetAliasedValueAttributeValue(this  Entity target, string attributeName)
        {
            return GetAliasedValueAttributeValue(target, null, null, attributeName);
        }

        public static int? GetOptionSetAttributeValue(Entity target1, Entity target2, Entity target3, string attributeName)
        {
            var qqq = GetAttribute(target1, target2, target3, attributeName);
            if (qqq != null)
                return ((OptionSetValue)qqq).Value;
            else
                return null;
        }

        public static int? GetOptionSetAttributeValue(this  Entity target, string attributeName)
        {
            return GetOptionSetAttributeValue(target, null, null, attributeName);
        }

        public static string GetStringAttributeValue(Entity target1, Entity target2, Entity target3, string attributeName)
        {
            var qqq = GetAttribute(target1, target2, target3, attributeName);
            if (qqq != null)
                return (string)qqq;
            else
                return "";
        }
        public static string GetStringAttributeValue(this  Entity target, string attributeName)
        {
            return GetStringAttributeValue(target, null, null, attributeName);
        }

        public static DateTime? GetDateTimeAttributeValue(Entity target1, Entity target2, Entity target3, string attributeName)
        {
            var qqq = GetAttribute(target1, target2, target3, attributeName);
            if (qqq != null)
                return (DateTime)qqq;
            else
                return null;
        }
        public static DateTime? GetDateTimeAttributeValue(this  Entity target, string attributeName)
        {
            return GetDateTimeAttributeValue(target, null, null, attributeName);
        }

        #endregion

        #region сравнение атрибутов

        public static bool ChangedAllTypesAttributeValue(Entity target1, Entity target2, string attributeName)
        {
            var value1 = Util.GetAttribute(target1, null, null, attributeName);
            var value2 = Util.GetAttribute(target2, null, null, attributeName);

            if (value1 == null && value2 == null)
                return false;
            else if ((value1 != null && value2 == null) || (value1 == null && value2 != null))
                return true;
            else
                if (value1 is int)
                    return ChangedIntAttributeValue(target1, target2, attributeName);
                else if (value1 is string)
                    return ChangedStringAttributeValue(target1, target2, attributeName);
                else if (value1 is OptionSetValue)
                    return ChangedOptionSetAttributeValue(target1, target2, attributeName);
                else if (value1 is decimal)
                    return ChangedDecimalAttributeValue(target1, target2, attributeName);
                else if (value1 is Money)
                    return ChangedMoneyAttributeValue(target1, target2, attributeName);
                else if (value1 is bool)
                    return ChangedBoolAttributeValue(target1, target2, attributeName);
                else if (value1 is DateTime)
                    return ChangedDateTimeAttributeValue(target1, target2, attributeName);
                else if (value1 is EntityReference)
                    return ChangedEntityReferenceAttributeValue(target1, target2, attributeName);
                else if (value1 is Guid)
                    return true;
                else
                    throw new Exception("Неизвестный тип [CompareAllTypesAttributeValue]");
        }

        public static bool ChangedIntAttributeValue(Entity target1, Entity target2, string attributeName)
        {
            var q1 = target1.GetIntAttributeValue(attributeName);
            var q2 = target2.GetIntAttributeValue(attributeName);

            return Util.EqualsInt(q1, q2) == false;
        }
        public static bool ChangedStringAttributeValue(Entity target1, Entity target2, string attributeName)
        {
            var q1 = target1.GetStringAttributeValue(attributeName);
            var q2 = target2.GetStringAttributeValue(attributeName);

            return Util.EqualsString(q1, q2) == false;
        }
        public static bool ChangedOptionSetAttributeValue(Entity target1, Entity target2, string attributeName)
        {
            var q1 = target1.GetOptionSetAttributeValue(attributeName);
            var q2 = target2.GetOptionSetAttributeValue(attributeName);

            return Util.EqualsInt(q1, q2) == false;
        }
        public static bool ChangedDecimalAttributeValue(Entity target1, Entity target2, string attributeName)
        {
            var q1 = target1.GetDecimalAttributeValue(attributeName);
            var q2 = target2.GetDecimalAttributeValue(attributeName);

            return Util.EqualsDecimal(q1, q2) == false;
        }
        public static bool ChangedMoneyAttributeValue(Entity target1, Entity target2, string attributeName)
        {
            var q1 = target1.GetMoneyAttributeValue(attributeName);
            var q2 = target2.GetMoneyAttributeValue(attributeName);

            return Util.EqualsDecimal(q1, q2) == false;
        }
        public static bool ChangedBoolAttributeValue(Entity target1, Entity target2, string attributeName)
        {
            var q1 = target1.GetBoolAttributeValue(attributeName);
            var q2 = target2.GetBoolAttributeValue(attributeName);

            return Util.EqualsBool(q1, q2) == false;
        }
        public static bool ChangedDateTimeAttributeValue(Entity target1, Entity target2, string attributeName)
        {
            var q1 = target1.GetDateTimeAttributeValue(attributeName);
            var q2 = target2.GetDateTimeAttributeValue(attributeName);

            return Util.EqualsDateTime(q1, q2) == false;
        }
        public static bool ChangedEntityReferenceAttributeValue(Entity target1, Entity target2, string attributeName)
        {
            var q1 = target1.GetEntityReferenceAttributeValue(attributeName);
            var q2 = target2.GetEntityReferenceAttributeValue(attributeName);

            return Util.EqualsEntityReference(q1, q2) == false;
        }


        public static bool EqualsAllTypes(object value1, object value2)
        {
            if (value1 == null && value2 == null)
                return true;
            else if ((value1 != null && value2 == null) || (value1 == null && value2 != null))
                return false;
            else
                if (value1 is int)
                    return EqualsInt((int)value1, (int)value2);
                else if (value1 is string)
                    return EqualsString((string)value1, (string)value2);
                else if (value1 is OptionSetValue)
                    return EqualsOptionSet((OptionSetValue)value1, (OptionSetValue)value2);
                else if (value1 is decimal)
                    return EqualsDecimal((decimal)value1, (decimal)value2);
                else if (value1 is Money)
                    return EqualsMoney((Money)value1, (Money)value2);
                else if (value1 is bool)
                    return EqualsBool((bool)value1, (bool)value2);
                else if (value1 is DateTime)
                    return EqualsDateTime((DateTime)value1, (DateTime)value2);
                else if (value1 is EntityReference)
                    return EqualsEntityReference((EntityReference)value1, (EntityReference)value2);
                else if (value1 is Guid)
                    return true;
                else
                    throw new Exception("Неизвестный тип [EqualsAllTypes]");
        }

        public static bool EqualsInt(int? value1, int? value2)
        {
            if (!value1.HasValue && !value2.HasValue)
                return true;
            else if ((value1.HasValue && !value2.HasValue) || (!value1.HasValue && value2.HasValue))
                return false;
            else
                return value1.Value == value2.Value;
        }
        public static bool EqualsString(string value1, string value2)
        {
            if (string.IsNullOrEmpty(value1) && string.IsNullOrEmpty(value2))
                return true;
            else if ((!string.IsNullOrEmpty(value1) && string.IsNullOrEmpty(value2)) || (string.IsNullOrEmpty(value1) && !string.IsNullOrEmpty(value2)))
                return false;
            else
                return value1 == value2;
        }
        public static bool EqualsOptionSet(OptionSetValue value1, OptionSetValue value2)
        {
            if (value1 == null && value2 == null)
                return true;
            else if ((value1 != null && value2 == null) || (value1 == null && value2 != null))
                return false;
            else
                return value1.Value == value2.Value;
        }
        public static bool EqualsDecimal(decimal? value1, decimal? value2)
        {
            if (!value1.HasValue && !value2.HasValue)
                return true;
            else if ((value1.HasValue && !value2.HasValue) || (!value1.HasValue && value2.HasValue))
                return false;
            else
                return value1.Value == value2.Value;
        }
        public static bool EqualsMoney(Money value1, Money value2)
        {
            if (value1 == null && value2 == null)
                return true;
            else if ((value1 != null && value2 == null) || (value1 == null && value2 != null))
                return false;
            else
                return value1.Value == value2.Value;
        }
        public static bool EqualsBool(bool? value1, bool? value2)
        {
            if (!value1.HasValue && !value2.HasValue)
                return true;
            else if ((value1.HasValue && !value2.HasValue) || (!value1.HasValue && value2.HasValue))
                return false;
            else
                return value1.Value == value2.Value;
        }
        public static bool EqualsDateTime(DateTime? value1, DateTime? value2)
        {
            if (!value1.HasValue && !value2.HasValue)
                return true;
            else if ((value1.HasValue && !value2.HasValue) || (!value1.HasValue && value2.HasValue))
                return false;
            else
                return value1.Value == value2.Value;
        }
        public static bool EqualsEntityReference(EntityReference value1, EntityReference value2)
        {
            if (value1 == null && value2 == null)
                return true;
            else if ((value1 != null && value2 == null) || (value1 == null && value2 != null))
                return false;
            else
                return value1.Id == value2.Id;
        }
        #endregion

        public static Entity Retrieve_Custom(this  IOrganizationService service, EntityReference entityRef, ColumnSet columns)
        {
            return Retrieve_Custom(service, entityRef.LogicalName, entityRef.Id, columns);
        }

        public static Entity Retrieve_Custom(this  IOrganizationService service, string entityName, Guid entityId, ColumnSet columns)
        {
            QueryExpression query = new QueryExpression()
            {
                EntityName = entityName,
                NoLock = true,
                ColumnSet = columns,
                Criteria = new FilterExpression(LogicalOperator.And),
                PageInfo = new PagingInfo()
                {
                    Count = 1,
                    PageNumber = 1,
                    ReturnTotalRecordCount = false
                }
            };

            var arr = new string[] { "appointment", "phonecall", "letter", "fax", "task", "email" };
            query.Criteria.AddCondition((arr.Contains(entityName) ? "activityid" : entityName + "id"), ConditionOperator.Equal, entityId);

            var result = service.RetrieveMultiple_Custom(query);

            if (result.Length == 0)
                throw new Exception(string.Format("Не найдено записи [{0}] с id = '{1}'", entityName, entityId));
            else if (result.Length == 1)
                return result[0];
            else
                throw new Exception(string.Format("Найдено более одной записи [{0}] с id = '{1}'", entityName, entityId));
        }

        public static Entity[] RetrieveMultiple_Custom(this IOrganizationService service, string fetch)
        {
            var response = (FetchXmlToQueryExpressionResponse)service.Execute(new FetchXmlToQueryExpressionRequest() { FetchXml = fetch });
            return service.RetrieveMultiple_Custom(response.Query);
        }

        public static Entity[] RetrieveMultiple_Custom(this  IOrganizationService service, QueryExpression query)
        {
            List<Entity> result = new List<Entity>();
            query.NoLock = true;

            var retrieved = service.RetrieveMultiple(query);
            result.AddRange(retrieved.Entities);

            if (retrieved.MoreRecords)
            {
                query.PageInfo.PageNumber++;
                result.AddRange(service.RetrieveMultiple_Custom(query));
            }

            return result.ToArray();
        }

        public static Entity[] QueryByAttribute(this  IOrganizationService service, string entityName, ColumnSet cols, IList<string> attributes, IList<object> values, OrderExpression[] orderExpressions)
        {
            QueryExpression query1 = new QueryExpression(entityName) { NoLock = true };

            if (cols != null)
                query1.ColumnSet = cols;
            else
                query1.ColumnSet = new ColumnSet();

            if (attributes != null && values != null && attributes.Count == values.Count)
            {
                query1.Criteria = new FilterExpression();
                query1.Criteria.FilterOperator = LogicalOperator.And;
                for (int i = 0; i < attributes.Count; i++)
                    query1.Criteria.AddCondition(attributes[i], ConditionOperator.Equal, values[i]);
            }

            if (orderExpressions != null)
                query1.Orders.AddRange(orderExpressions);

            return service.RetrieveMultiple_Custom(query1);
        }

        public static EntityCollection QueryByAttribute(this  IOrganizationService service, string entityName, ColumnSet cols,
            Dictionary<string, object> attributes, OrderExpression[] orderExpressions, int? top)
        {
            QueryExpression query = new QueryExpression(entityName) { NoLock = true };

            if (cols != null)
                query.ColumnSet = cols;
            else
                query.ColumnSet = new ColumnSet();

            if (attributes != null)
            {
                query.Criteria = new FilterExpression();
                query.Criteria.FilterOperator = LogicalOperator.And;
                foreach (var item in attributes)
                    query.Criteria.AddCondition(item.Key, ConditionOperator.Equal, item.Value);
            }

            if (orderExpressions != null)
                query.Orders.AddRange(orderExpressions);

            if (top.HasValue)
                query.PageInfo = new PagingInfo() { PageNumber = 1, Count = top.Value };

            return service.RetrieveMultiple(query);
        }

        public static Dictionary<int, string> GetPickList(this IOrganizationService service, string entityLogicalName, string fieldName)
        {
            RetrieveAttributeRequest request = new RetrieveAttributeRequest();
            request.EntityLogicalName = entityLogicalName;
            request.LogicalName = fieldName;
            RetrieveAttributeResponse response = (RetrieveAttributeResponse)service.Execute(request);

            EnumAttributeMetadata picklist = (EnumAttributeMetadata)response.AttributeMetadata;
            Dictionary<int, string> options = new Dictionary<int, string>();

            foreach (var opt in picklist.OptionSet.Options)
            {
                options.Add(opt.Value.Value, opt.Label.UserLocalizedLabel.Label);
            }
            return options;
        }

        public static Dictionary<int, Tuple<string, int>> GetStatuscodeList(this IOrganizationService service, string entityLogicalName)
        {
            RetrieveAttributeRequest retrieveAttributeRequest = new RetrieveAttributeRequest
            {
                EntityLogicalName = entityLogicalName,
                LogicalName = "statuscode",
                RetrieveAsIfPublished = true
            };

            RetrieveAttributeResponse retrieveAttributeResponse = (RetrieveAttributeResponse)service.Execute(retrieveAttributeRequest);
            StatusAttributeMetadata statusAttrMetadata = (StatusAttributeMetadata)retrieveAttributeResponse.AttributeMetadata;
            Dictionary<int, Tuple<string, int>> options = new Dictionary<int, Tuple<string, int>>();
            foreach (StatusOptionMetadata statusMeta in statusAttrMetadata.OptionSet.Options)
            {
                options.Add(statusMeta.Value.Value, new Tuple<string, int>(statusMeta.Label.UserLocalizedLabel.Label, statusMeta.State.Value));
            }
            return options;
        }

        public static Dictionary<int, string> GetStatecodeList(this IOrganizationService service, string entityLogicalName)
        {
            RetrieveAttributeRequest retrieveAttributeRequest = new RetrieveAttributeRequest
            {
                EntityLogicalName = entityLogicalName,
                LogicalName = "statecode",
                RetrieveAsIfPublished = true
            };

            RetrieveAttributeResponse retrieveAttributeResponse = (RetrieveAttributeResponse)service.Execute(retrieveAttributeRequest);
            StateAttributeMetadata statusAttrMetadata = (StateAttributeMetadata)retrieveAttributeResponse.AttributeMetadata;
            Dictionary<int, string> options = new Dictionary<int, string>();
            foreach (StateOptionMetadata statusMeta in statusAttrMetadata.OptionSet.Options)
            {
                options.Add(statusMeta.Value.Value, statusMeta.Label.UserLocalizedLabel.Label);
            }
            return options;
        }

        /// <summary>
        /// Возвращает Этап согласования по его номеру в дереве
        /// </summary>
        /// <param name="stageCode">Номер в дереве</param>
        /// <returns></returns>
        public static EntityReference GetStageRefByCode(this IOrganizationService service, string stageCode)
        {
            EntityReference er = null;
            if (!String.IsNullOrEmpty(stageCode))
            {
                QueryExpression queryStatuscode = new QueryExpression("new_statuscode")
                {
                    Criteria = new FilterExpression()
                    {
                        Conditions =
                        {
                            new ConditionExpression()
                            {
                                AttributeName = "new_number",
                                Operator = ConditionOperator.Equal,
                                Values = { stageCode }
                            }
                        }
                    }
                };
                var statuses = service.RetrieveMultiple_Custom(queryStatuscode);
                if (statuses != null && statuses.Length > 0)
                {
                    er = new EntityReference(statuses[0].LogicalName, statuses[0].Id);
                }
            }
            return er;
        }

        public static Uri CreateLinkForEntity(string ServerName, string OrganizationName, int objectTypeCode, Guid objectId)
        {
            if (!string.IsNullOrWhiteSpace(OrganizationName))
                ServerName += "/" + OrganizationName;

            return new Uri(Uri.EscapeUriString(string.Format("{0}/main.aspx?etc={1}&id={2}&pagetype=entityrecord&rof=false&extraqs=?", ServerName, objectTypeCode, objectId.ToString("B"))), UriKind.Absolute);
        }

        public static Uri CreateLinkForEntity2(string ServerNameWithOrgName, string objectTypeName, Guid objectId)
        {
            return CreateLinkForEntity2(ServerNameWithOrgName, "", objectTypeName, objectId);
        }

        public static Uri CreateLinkForEntity2(string ServerName, string OrganizationName, string objectTypeName, Guid objectId)
        {
            if (!string.IsNullOrWhiteSpace(OrganizationName))
                ServerName += "/" + OrganizationName;

            return new Uri(string.Format("{0}/main.aspx?etn={1}&pagetype=entityrecord&id=%7B{2}%7D&rof=false&extraqs=?", ServerName, objectTypeName, objectId.ToString()), UriKind.Absolute);
        }

        //public static Uri CreateLinkForEntity(IOrganizationService service, EntityReference entity)
        //{
        //    var crmSettings = new CrmSettings(service);
        //    return new Uri(string.Format("{0}/main.aspx?etn={1}&pagetype=entityrecord&id=%7B{2}%7D&rof=false&extraqs=?", crmSettings.CrmUrlWithPortAndOrgname, entity.LogicalName, entity.Id.ToString()), UriKind.Absolute);
        //}


        public static OrganizationServiceProxy GetCrmService(Uri serviceUri, ClientCredentials clientCredentials)
        {
            OrganizationServiceProxy serviceProxy = new OrganizationServiceProxy(serviceUri, null, clientCredentials, null);
            serviceProxy.EnableProxyTypes();
            //serviceProxy.Timeout = new TimeSpan(1, 0, 0);
            return serviceProxy;
        }

        public static ClientCredentials GetCredentials_UsernamePassword(string username, string password)
        {
            ClientCredentials credentials = new ClientCredentials();
            credentials.UserName.UserName = username;
            credentials.UserName.Password = password;
            return credentials;
        }

        public static Guid WhoAmI(this IOrganizationService service)
        {
            WhoAmIRequest req = new WhoAmIRequest();
            var resp = (WhoAmIResponse)service.Execute(req);
            return resp.UserId;
        }
        //public static List<string> GetUserAllRoles(this IOrganizationService service, Guid userId)
        //{
        //    List<string> RoleNames = new List<string>();
        //    QueryExpression Roles_query = new QueryExpression()
        //    {
        //        NoLock = true,
        //        ColumnSet = new ColumnSet(false),
        //        EntityName = "systemuser",
        //        Criteria = new FilterExpression(LogicalOperator.And)
        //        {
        //            Conditions =
        //            {
        //                new ConditionExpression("systemuserid",ConditionOperator.Equal,userId)
        //            }
        //        },
        //        LinkEntities =
        //        {
        //            new LinkEntity("systemuser","teammembership","systemuserid","systemuserid",JoinOperator.LeftOuter)
        //            {
        //                Columns=new ColumnSet(false),
        //                LinkEntities=
        //                {
        //                    new LinkEntity("teammembership","teamroles","teamid","teamid",JoinOperator.LeftOuter)
        //                    {
        //                        Columns=new ColumnSet(false),
        //                        LinkEntities={
        //                            new LinkEntity("teamroles","role","roleid","roleid",JoinOperator.LeftOuter)
        //                            {
        //                                EntityAlias="TRole",
        //                                Columns=new ColumnSet("name","roleid")
        //                            }
        //                        }
        //                    }
        //                }
        //            },
        //            new LinkEntity("systemuser","systemuserroles","systemuserid","systemuserid",JoinOperator.LeftOuter)
        //            {
        //                Columns=new ColumnSet(false),
        //                LinkEntities=
        //                {
        //                    new LinkEntity("systemuserroles","role","roleid","roleid",JoinOperator.LeftOuter)
        //                    {
        //                         EntityAlias="SRole",
        //                         Columns=new ColumnSet("name","roleid")                               
        //                    }
        //                }
        //            }
        //        }
        //    };
        //    try
        //    {
        //        foreach (var item in service.RetrieveMultiple_Custom(Roles_query))
        //        {
        //            if (!string.IsNullOrEmpty(item.GetStringAttributeValue("SRole.name")))
        //                RoleNames.Add(item.GetStringAttributeValue("SRole.name"));
        //            if (!string.IsNullOrEmpty(item.GetStringAttributeValue("TRole.name")))
        //                RoleNames.Add(item.GetStringAttributeValue("TRole.name"));
        //        }
        //        return RoleNames;
        //    }
        //    catch (Exception ex)
        //    {
        //        StringBuilder log = new StringBuilder("Ошибка При получении ролей у полъзователя [" + userId.ToString() + "]");
        //        log.AppendLine(string.Format("MethodName:{0}", "GetUserAllRoles"));
        //        log.AppendLine(string.Format("Error:{0}", ex.Message));
        //        log.ToString().WriteToLog("Ошибка При получении ролей у полъзователя", EntityHelper.ErrorSource.Plugin, System.Diagnostics.EventLogEntryType.Error, service);
        //        return null;
        //        throw;
        //    }

        //}

        public static void DeactivateRecord(this IOrganizationService service, Entity record)
        {
            service.DeactivateRecord(record.LogicalName, record.Id);
        }
        public static void DeactivateRecordWSC(this IOrganizationService service, Entity record, int statuscode)
        {
            var setStateReq = new SetStateRequest();
            setStateReq.EntityMoniker = new EntityReference(record.LogicalName, record.Id);
            setStateReq.State = new OptionSetValue(1);
            setStateReq.Status = new OptionSetValue(statuscode);
            var response = (SetStateResponse)service.Execute(setStateReq);
        }
        public static void DeactivateRecord(this IOrganizationService service, Entity record, int statecode, int statuscode)
        {
            var setStateReq = new SetStateRequest();
            setStateReq.EntityMoniker = new EntityReference(record.LogicalName, record.Id);
            setStateReq.State = new OptionSetValue(statecode);
            setStateReq.Status = new OptionSetValue(statuscode);
            var response = (SetStateResponse)service.Execute(setStateReq);
        }
        //public static void WinRecord(this IOrganizationService service, Entity record, int statuscode)
        //{
        //    // Close the opportunities as 'Won'
        //    WinOpportunityRequest winRequest = new WinOpportunityRequest()
        //    {
        //        OpportunityClose = new OpportunityClose()
        //        {
        //            OpportunityId = new EntityReference
        //            {
        //                Id = record.Id,
        //                LogicalName = record.LogicalName
        //            },
        //        },
        //        Status = new OptionSetValue(statuscode)
        //    };
        //    service.Execute(winRequest);


        //}

        public static void DeactivateRecord(this IOrganizationService service, string recordLogicalName, Guid recordId)
        {
            var setStateReq = new SetStateRequest();
            setStateReq.EntityMoniker = new EntityReference(recordLogicalName, recordId);
            setStateReq.State = new OptionSetValue(1);
            setStateReq.Status = new OptionSetValue(2);
            var response = (SetStateResponse)service.Execute(setStateReq);
        }

        public static string FormatedRusDateTime(this DateTime? dt)
        {
            if (dt.HasValue)
                return dt.Value.ToString("dd.MM.yyyy");
            else
                return "";
        }

        public static string FormatedDecimalPoint(this decimal? d)
        {
            if (d.HasValue)
                return d.Value.ToString().Replace(",", ".");
            else
                return "";
        }

        public static string FormatedDecimalPoint(this double? d)
        {
            if (d.HasValue)
                return d.Value.ToString().Replace(",", ".");
            else
                return "";
        }

        //public static string CreateErrorJSON(string errorText, string name)
        //{
        //    var result = new
        //    {
        //        HasError = true,
        //        ErrorText = errorText,
        //        Name = name
        //    };
        //    return (new System.Web.Script.Serialization.JavaScriptSerializer()).Serialize(result);
        //}

        public static void RevokeAccess(this IOrganizationService service, EntityReference principal, EntityReference target)
        {
            RevokeAccessRequest req = new RevokeAccessRequest()
            {
                Revokee = principal,
                Target = target
            };
            var resp = (RevokeAccessResponse)service.Execute(req);
        }

        public static void GrantAccess(this IOrganizationService service, AccessRights accessRight, EntityReference principal, EntityReference target)
        {
            GrantAccessRequest req = new GrantAccessRequest()
            {
                PrincipalAccess = new PrincipalAccess()
                {
                    AccessMask = accessRight,
                    Principal = principal
                },
                Target = target
            };

            var resp = (GrantAccessResponse)service.Execute(req);
        }

        public static int GetNumber(IOrganizationService crmService, string objectName, string relativeName)
        {
            int number = 1;

            {
                QueryExpression q = new QueryExpression("new_numeration")
                {
                    ColumnSet = new ColumnSet(),
                    NoLock = true,
                    TopCount = 1
                };

                var qq = crmService.RetrieveMultiple(q);
                if (qq.Entities.Count > 0)
                {
                    var qqq = qq[0];
                    qqq["new_temp"] = DateTime.Now;
                    crmService.Update(qqq);
                }
            }

            QueryExpression query1 = new QueryExpression("new_numeration")
            {
                ColumnSet = new ColumnSet("new_numberabsolute"),
                Criteria = new FilterExpression(LogicalOperator.And)
            };
            query1.Criteria.AddCondition("new_name", ConditionOperator.Equal, objectName);
            if (!string.IsNullOrEmpty(relativeName))
                query1.Criteria.AddCondition("new_numberrelative", ConditionOperator.Equal, relativeName);

            var new_numerations = crmService.RetrieveMultiple(query1);

            if (new_numerations.Entities.Count > 0)
            {
                var new_numeration = new_numerations.Entities[0];
                number = Convert.ToInt32(new_numeration.GetStringAttributeValue("new_numberabsolute"));
                number++;
                new_numeration["new_numberabsolute"] = number.ToString();
                crmService.Update(new_numeration);
            }
            else
            {
                Entity new_numeration = new Entity("new_numeration");
                new_numeration["new_name"] = objectName;
                new_numeration["new_numberabsolute"] = number.ToString();
                if (!string.IsNullOrEmpty(relativeName))
                    new_numeration["new_numberrelative"] = relativeName;

                crmService.Create(new_numeration);
            }

            return number;
        }

        //public static bool UserIsBusy(IOrganizationService crmService, Guid userId, DateTime startDT, DateTime endDT)
        //{
        //    SystemUser user = (SystemUser)crmService.Retrieve("systemuser", userId, new ColumnSet("calendarid"));

        //    return UserIsBusy(crmService, user, startDT, endDT);
        //}
        //public static TimeInfo[] UserTimeInfo(IOrganizationService crmService, Guid userId, DateTime startDT, DateTime endDT)
        //{
        //    SystemUser user = (SystemUser)crmService.Retrieve("systemuser", userId, new ColumnSet("calendarid"));

        //    return UserTimeInfo(crmService, user, startDT, endDT);
        //}
        //public static TimeInfo[] UserTimeInfo(IOrganizationService crmService, SystemUser user, DateTime startDT, DateTime endDT)
        //{
        //    ExpandCalendarRequest req = new ExpandCalendarRequest();
        //    req.CalendarId = user.CalendarId.Id;
        //    req.Start = startDT;
        //    req.End = endDT;
        //    ExpandCalendarResponse resp = (ExpandCalendarResponse)crmService.Execute(req);

        //    return (TimeInfo[])resp.result;
        //}
        //public static bool UserIsBusy(IOrganizationService crmService, SystemUser user, DateTime startDT, DateTime endDT)
        //{
        //    ExpandCalendarRequest req = new ExpandCalendarRequest();
        //    req.CalendarId = user.CalendarId.Id;
        //    req.Start = startDT;
        //    req.End = endDT;
        //    ExpandCalendarResponse resp = (ExpandCalendarResponse)crmService.Execute(req);

        //    return ((TimeInfo[])resp.result).Count(x => x.SubCode == SubCode.Vacation) > 0;
        //    //.Select(x => new
        //    //{   
        //    //    Start = (x.Start ?? DateTime.Now).ToLocalTime(),
        //    //    End = (x.End ?? DateTime.Now).ToLocalTime(),
        //    //    x.SubCode
        //    //}).OrderBy(x => x.Start).ToArray();


        //}
        public static void SendEMail(EntityReference[] to, EntityReference[] from, string subject, string description, EntityReference regardingObject, IOrganizationService service)
        {
            Entity email = new Entity("email");

            List<Entity> toList = new List<Entity>();
            foreach (EntityReference entity in to)
            {
                Entity p = new Entity("activityparty");
                p["partyid"] = new EntityReference(entity.LogicalName, entity.Id);

                toList.Add(p);
            }
            email["to"] = toList.ToArray();

            if (from != null && from.Length > 0)
            {
                List<Entity> fromList = new List<Entity>();
                foreach (EntityReference entity in from)
                {
                    Entity p = new Entity("activityparty");
                    p["partyid"] = new EntityReference(entity.LogicalName, entity.Id);

                    fromList.Add(p);
                }
                email["from"] = fromList.ToArray();
            }

            if (regardingObject != null)
                email["regardingobjectid"] = regardingObject;

            if (!String.IsNullOrEmpty(subject))
                email["subject"] = subject;

            if (!String.IsNullOrEmpty(description))
                email["description"] = description;

            email.Id = service.Create(email);

            SendEmailRequest sendEmailreq = new SendEmailRequest
            {
                EmailId = email.Id,
                TrackingToken = "",
                IssueSend = true
            };
            SendEmailResponse res = (SendEmailResponse)service.Execute(sendEmailreq);
        }

        // public static void SendEmailUsingTemplate(EntityReference entitySenderRef, object from, List<object> to, List<object> copyReceiver, string templateName,
        //IOrganizationService service, Dictionary<string, string> replacesTitle, Dictionary<string, string> replacesBody, Dictionary<string, byte[]> attaches, bool sendMail)
        // {
        //     string step = "0";
        //     try
        //     {
        //         // Получаем GUID шаблона по номеру, указанному в шаблоне поле Описание
        //         QueryByAttribute query = new QueryByAttribute("template");
        //         query.ColumnSet = new ColumnSet();
        //         query.AddAttributeValue("title", templateName);

        //         step = "1";
        //         EntityCollection coll = service.RetrieveMultiple(query);
        //         if (coll.Entities.Count > 0)
        //         {
        //             Guid templateId = (Guid)coll.Entities[0].Id;
        //             step = "2";

        //             InstantiateTemplateRequest req = new InstantiateTemplateRequest
        //             {
        //                 TemplateId = templateId,
        //                 ObjectId = entitySenderRef.Id,
        //                 ObjectType = entitySenderRef.LogicalName
        //             };

        //             step = "3 (выполнение запроса)";
        //             InstantiateTemplateResponse resp = (InstantiateTemplateResponse)service.Execute(req);
        //             step = "4";
        //             Entity email = resp.EntityCollection.Entities[0];

        //             if (from != null)
        //             {
        //                 List<Entity> fromList = new List<Entity>();
        //                 {
        //                     Entity activityparty = new Entity("activityparty");

        //                     if (from is EntityReference)
        //                         activityparty["partyid"] = from as EntityReference;
        //                     else if (from is string)
        //                         activityparty["addressused"] = from as string;

        //                     fromList.Add(activityparty);
        //                 }
        //                 email["from"] = fromList.ToArray();
        //             }

        //             if (to != null)
        //             {
        //                 List<Entity> apTos = new List<Entity>();

        //                 foreach (var _to in to)
        //                 {
        //                     Entity apTo = new Entity("activityparty");

        //                     if (_to is EntityReference)
        //                         apTo["partyid"] = _to as EntityReference;
        //                     else if (_to is string)
        //                         apTo["addressused"] = _to as string;

        //                     apTos.Add(apTo);
        //                 }

        //                 email["to"] = apTos.ToArray();
        //             }

        //             if (copyReceiver != null)
        //             {
        //                 List<Entity> apCcs = new List<Entity>();

        //                 foreach (var _cc in copyReceiver)
        //                 {
        //                     Entity apCc = new Entity("activityparty");

        //                     if (_cc is EntityReference)
        //                         apCc["partyid"] = _cc as EntityReference;
        //                     else if (_cc is string)
        //                         apCc["addressused"] = _cc as string;

        //                     apCcs.Add(apCc);
        //                 }

        //                 email["cc"] = apCcs.ToArray();
        //             }

        //             step = "5";
        //             email["directioncode"] = true;

        //             if (replacesTitle != null)
        //                 foreach (var item in replacesTitle)
        //                     email["subject"] = email.GetStringAttributeValue("subject").Replace(item.Key, item.Value);

        //             if (replacesBody != null)
        //                 foreach (var item in replacesBody)
        //                     email["description"] = email.GetStringAttributeValue("description").Replace(item.Key, item.Value);

        //             step = "6";
        //             email["regardingobjectid"] = entitySenderRef;

        //             if (attaches != null)
        //             {
        //                 step = "61";
        //                 if (attaches.Count > 0)
        //                 {
        //                     step = "611";
        //                     email["email_activity_mime_attachment"] = attaches.Select(item => AttachFileToEmail(item.Key, item.Value));
        //                 }
        //             }

        //             step = "7";
        //             Guid emailId = service.Create(email);

        //             if (sendMail)
        //             {
        //                 SendEmailRequest sendReq = new SendEmailRequest
        //                 {
        //                     EmailId = emailId,
        //                     IssueSend = true,
        //                     TrackingToken = ""
        //                 };

        //                 step = "8";
        //                 service.Execute(sendReq);
        //             }
        //         }
        //         else
        //         {
        //             throw new Exception("Не найден шаблон с именем " + templateName.ToString());
        //         }
        //     }
        //     catch (Exception ex)
        //     {
        //         throw new Exception(string.Format("Step (SendEmailUsingTemplate) = {0}{1}Ошибка: {2}", step, Environment.NewLine, ex.ToString()));
        //     }
        // }

        // /// <summary>
        // /// подготовливает, создает и отправляет письмо
        // /// </summary>
        // /// <param name="serice">сервис</param>
        // /// <param name="mail">объект - email</param>
        // /// <param name="attaches">файлы</param>
        // /// <param name="sendNow">отправить сейчас?</param>
        //public static void SendEmail(IOrganizationService service, Entity mail, Dictionary<string, byte[]> attaches = null, bool sendNow = false)
        //{
        //    // прикрепляем файлы
        //    Guid emailId = service.Create(mail);

        //    foreach (var file in attaches)
        //    {
        //        service.Create(AttachFileToEmail(file.Key, file.Value, emailId));
        //    }

        //    if (sendNow)
        //    {
        //        SendEmailRequest sendReq = new SendEmailRequest
        //        {
        //            EmailId = emailId,
        //            IssueSend = true,
        //            TrackingToken = ""
        //        };
        //        service.Execute(sendReq);
        //    }
        //}
        //public static ActivityMimeAttachment AttachFileToEmail(String fileName, byte[] byteData, Guid? emailId = null)
        //{
        //    // Encode the data using base64.
        //    var encodedData = Convert.ToBase64String(byteData);
        //    var extension = System.IO.Path.GetExtension(fileName).ToLower();

        //    String mimeType = null;

        //    switch (extension)
        //    {
        //        case ".pdf":
        //            mimeType = "application/pdf";
        //            break;
        //        case ".xls":
        //            mimeType = "application/vnd.ms-excel";
        //            break;
        //        case ".doc":
        //            mimeType = "application/msword";
        //            break;
        //        case ".xlsx":
        //            mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
        //            break;
        //        case ".docx":
        //            mimeType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
        //            break;

        //    }

        //    var sampleAttachment = new ActivityMimeAttachment
        //    {
        //        ObjectId = new EntityReference(Email.EntityLogicalName, (Guid)emailId),
        //        ObjectTypeCode = Email.EntityLogicalName,
        //        FileName = fileName,
        //        MimeType = mimeType,
        //        Body = encodedData,
        //    };

        //    return sampleAttachment;
        //}

        public static Entity AttachFileToEmail(IOrganizationService service, String fileName, byte[] byteData, Guid emailId)
        {
            var encodedData = Convert.ToBase64String(byteData);
            var extension = System.IO.Path.GetExtension(fileName).ToLower();

            String mimeType = null;

            switch (extension)
            {
                case ".pdf":
                    mimeType = "application/pdf";
                    break;
                case ".xls":
                    mimeType = "application/vnd.ms-excel";
                    break;
                case ".xlsx":
                    mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                    break;
                case ".doc":
                    mimeType = "application/msword";
                    break;
                case ".docx":
                    mimeType = "application/msword";
                    break;
            }
            Entity activitymimeattachment = new Entity("activitymimeattachment");
            activitymimeattachment["filename"] = fileName;
            activitymimeattachment["mimetype"] = mimeType;
            activitymimeattachment["subject"] = "test";
            activitymimeattachment["body"] = encodedData;
            activitymimeattachment["objectid"] = new EntityReference("email", emailId);
            activitymimeattachment["objecttypecode"] = 4202;
            return activitymimeattachment;
        }

        public static void SendEMail(object[] to, EntityReference[] from, IOrganizationService service, string subject, string description, EntityReference regardingObject, Dictionary<string, byte[]> attaches, bool sendMail)
        {
            string step = "0";
            try
            {
                step = "1";
                Entity email = new Entity("email");

                if (to != null)
                {
                    List<Entity> apTos = new List<Entity>();

                    foreach (var _to in to)
                    {
                        Entity apTo = new Entity("activityparty");

                        if (_to is EntityReference)
                            apTo["partyid"] = _to as EntityReference;
                        else if (_to is string)
                            apTo["addressused"] = _to as string;

                        apTos.Add(apTo);
                    }

                    email["to"] = apTos.ToArray();
                }

                if (from != null && from.Length > 0)
                {
                    List<Entity> fromList = new List<Entity>();
                    foreach (EntityReference entity in from)
                    {
                        Entity p = new Entity("activityparty");
                        p["partyid"] = new EntityReference(entity.LogicalName, entity.Id);

                        fromList.Add(p);
                    }
                    email["from"] = fromList.ToArray();
                }

                step = "2";
                email["directioncode"] = true;

                if (!String.IsNullOrEmpty(subject))
                    email["subject"] = subject;

                if (!String.IsNullOrEmpty(description))
                    email["description"] = description;

                step = "3";
                email["regardingobjectid"] = regardingObject;

                step = "4";
                Guid emailId = service.Create(email);

                step = "5";
                if (attaches != null)
                {

                    for (int i = 0; i < attaches.Count; i++)
                    {
                        service.Create(AttachFileToEmail(service, attaches.Keys.ElementAt(i), attaches.Values.ElementAt(i), emailId));
                    }
                }

                if (sendMail)
                {
                    SendEmailRequest sendReq = new SendEmailRequest
                    {
                        EmailId = emailId,
                        IssueSend = true,
                        TrackingToken = ""
                    };

                    step = "6";
                    service.Execute(sendReq);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Step (SendEmail) = {0}{1}Ошибка: {2}", step, Environment.NewLine, ex.ToString()));
            }
        }

        /// <summary>
        /// выдает список типов документов, необходимых для проверки поставщика
        /// </summary>
        /// <param name="service">текущий контекст</param>
        /// <param name="supplierEntity">поставщик</param>
        /// <returns></returns>
        public static string[] GetDocTypeList(Entity supplierEntity)
        {
            if (supplierEntity != null && (supplierEntity.GetBoolAttributeValue("new_suppliercargomachinery") ?? false))
                return new string[] { "Заключение УОБ по поставщику", "Заключение ЮУ по поставщику" };
            return new string[] { "Заключение УОБ по поставщику" };
        }

        public static string[] GetAllDocTypeList()
        {
            return new string[] { "Заключение УОБ по поставщику", "Заключение ЮУ по поставщику", "Рейтинг поставщика" };
        }
    }
}
